package syntaxclass;

import java.util.LinkedList;
import java.util.List;

import polyglot.ast.Block;
import polyglot.ast.CanonicalTypeNode;
import polyglot.ast.Catch;
import polyglot.ast.Expr;
import polyglot.ast.Formal;
import polyglot.ast.LocalDecl;
import polyglot.ast.MethodDecl;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Stmt;
import polyglot.ast.Try;
import polyglot.ast.TypeNode;
import polyglot.ext.jl.parse.Name;
import polyglot.types.Flags;
import polyglot.util.Position;
import polyglot.util.TypedList;
import xaj.ast.XAJNodeFactory;
import xaj.ast.XAJOps;
import abc.aspectj.ast.AdviceDecl;
import abc.aspectj.ast.AdviceDecl_c;
import abc.aspectj.ast.AdviceSpec;
import abc.aspectj.ast.ClassTypeDotId;
import abc.aspectj.ast.MethodPattern;
import abc.aspectj.ast.ModifierPattern;
import abc.aspectj.ast.Pointcut;
import abc.aspectj.ast.ProceedCall;
import abc.aspectj.ast.ThrowsPattern;
import abc.aspectj.ast.TypePatternExpr;

public class AJSynchroMethod extends AdviceDecl_c implements XAJOps, AdviceDecl {
	
	private MethodDecl methodhead;
	private Name name;
	private List listbefore, listafter;
	
	
	public AJSynchroMethod(Position pos, MethodDecl m, Name lock, List b, List a) {
		super(pos, Flags.NONE, null/*AdviceSpec*/, null/*throws_opt*/,
				null/*pointcut_expr*/, null/*method_body*/);
		methodhead = m;
		name = lock;
		listbefore = b;
		listafter = a;
	}
	
	@Grammar extends advice_declaration {
		AJSynchroMethod ::= "synchronize" "(" m = method_header ")" 
		                    "with" id = simple_name
		                    "{" ab = AJBefore 
		                    aa= AJAfter "}" {
			RESULT = new AJSynchroMethod(parser.pos(m), m, id, ab, aa);
		};
		AJBefore ::= "before" ":" b = block_statements {RESULT = b;};
		AJBefore ::= {RESULT = new TypedList(new LinkedList(), Stmt.class, false);};
		AJAfter ::= "after" ":" b = block_statements {RESULT = b;};
		AJAfter ::= {RESULT = new TypedList(new LinkedList(), Stmt.class, false);};
	}
	
	public Node desugar(/*Context ct,*/ NodeFactory ft) {
		XAJNodeFactory nf = (XAJNodeFactory) ft;
		
		AdviceSpec spec = nf.Around(position(), methodhead.returnType(),
				//methodhead.formals());
				new TypedList(new LinkedList(), Formal.class, false));
		
		List modifierlist = new TypedList(new LinkedList(), ModifierPattern.class, false);
		modifierlist.add(nf.ModifierPattern(null,methodhead.flags(),true));
		
		TypePatternExpr typepattern;
		TypeNode type = methodhead.returnType();
		if(type instanceof CanonicalTypeNode) {
			typepattern = nf.TPEType(position(),type);
		} else {			
			typepattern = nf.TPERefTypePat(position(), nf.RTPName(position(),
					nf.SimpleNamePattern(position(), type.name())));
		}		
		ClassTypeDotId classtype = nf.ClassTypeDotId(position(),
				nf.CPEUniversal(position()),//Isso deve ser mudado para a classe base.
				nf.SimpleNamePattern(position(), methodhead.name()));
		List throwslist = new TypedList(new LinkedList(), ThrowsPattern.class, false); 
		List listformal = methodhead.formals();
		Formal f;
		for(int i = 0; i < listformal.size(); ++i) {
			f = (Formal)listformal.get(i);
			ThrowsPattern t = nf.ThrowsPattern(position(),
					nf.CPEName(position(), nf.SimpleNamePattern(position(), f.name())),
					true);			
			throwslist.add(t);
		}		
		MethodPattern method = nf.MethodPattern(position(), modifierlist,
				typepattern, classtype, methodhead.formals(), throwslist);		
		Pointcut pointc = nf.PCExecution(position(), method);
		List listStm = new TypedList(new LinkedList(), Stmt.class, false);		
		Stmt methodLock = nf.Eval(position(), nf.Call(position(), name.toExpr(), "lock"));
		listStm.add(methodLock);
		listStm.addAll(listbefore);
		ProceedCall proceedCall = nf.ProceedCall(position(),
				nf.This(position()),
				new TypedList(new LinkedList(), Expr.class, false));
		LocalDecl local = nf.LocalDecl(position(), Flags.NONE, methodhead.returnType(),
				"aux_aux", proceedCall);
		listStm.add(local);
		listStm.addAll(listafter);
		listStm.add(nf.Return(position(), nf.AmbExpr(position(), "aux_aux")));
		Block tryBlock = nf.Block(position(), listStm);
		Stmt stm = nf.Eval(position(), nf.Call(position(), name.toExpr(), "unlock"));
		Block finallyBlock = nf.Block(position(), stm);
		Try trystm = nf.Try(position(), tryBlock,
				new TypedList(new LinkedList(), Catch.class, false),
				finallyBlock);	
		AdviceDecl advice = nf.AdviceDecl(position(), flags(), spec, 
				new TypedList(new LinkedList(), TypeNode.class, false),
				pointc, nf.Block(position(), trystm));
		
		return advice;
	}	
}
